package cn.catsdad.convert;

import cn.catsdad.convert.bean.CovertConfig;
import cn.catsdad.util.FileUtil;
import cn.catsdad.util.ResourceUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter;

import java.io.File;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author xuyl
 * @version V1.0.0 2024/12/31
 * @since V100R001
 */
public class ConvertToBatch {

    private static final String FIRST_REGEX = "INSERT INTO\\s+[\\w\\s.\"()]+?\\s+VALUES\\s*\\(";
    private static final String BATCH_REGEX = ";\\r?\\nINSERT\\s+INTO\\s.*VALUES";
    private static final String TABLE_REGEX = "\"([^\"]+)\"\\.(\"[^\"]+\")";
    private static final CovertConfig covertConfig = CovertConfig.getInstance();

    public static void convert(){
        if (covertConfig.getThreadCount()>0){
            doConvertAsync();
        } else {
            doConvert();
        }
    }

    public static void doConvert() {
        List<File> files = FileUtil.readDir(ResourceUtil.readResource("filePath", String.class));
        for (File file : files) {
            fileConvert(file);
        }
    }

    private static void fileConvert(File file) {
        FileReader fileReader = new FileReader(file);
        List<String> allReadLines = fileReader.readLines();
        Map<String, List<String>> tableLinesMap = groupByTable(allReadLines);
        for (String tableName : tableLinesMap.keySet()) {
            List<String> tableLines = tableLinesMap.get(tableName);
            appendToFile(tableName, tableLines);
        }
    }

    public static void doConvertAsync() {
        long startTime = System.currentTimeMillis();
        List<File> files = FileUtil.readDir(ResourceUtil.readResource("filePath", String.class));
        ExecutorService executor = Executors.newFixedThreadPool(covertConfig.getThreadCount());
        List<Future<?>> futures = new ArrayList<>();
        for (File file : files) {
            if (file.getName().contains("sql")) {
                Future<?> future = executor.submit(() -> {
                    fileConvert(file);
                });
                futures.add(future);
            }
        }
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        executor.shutdown();
        System.out.println("运行耗时：" + (System.currentTimeMillis() - startTime) + ",分配线程数量：" + covertConfig.getThreadCount());
    }

    private static Map<String, List<String>> groupByTable(List<String> allReadLines) {
        Map<String, List<String>> result = new HashMap<>();
        Pattern pattern = Pattern.compile(TABLE_REGEX);
        String lastTableName = null;
        String lastLine = null;
        for (String readLine : allReadLines) {
            Matcher matcher = pattern.matcher(readLine);
            if (matcher.find()) {
                String tableName = matcher.group();
                if (tableName.contains("\"")) tableName = tableName.replace("\"", "");
                if (tableName.contains(".")) {
                    String[] schemaAndTableName = tableName.split("\\.");
                    tableName = schemaAndTableName[1];
                }
                List<String> linesOfTable = result.computeIfAbsent(tableName, k -> new ArrayList<>());
                linesOfTable.add(readLine);
                lastTableName = tableName;
                lastLine = readLine;
            } else if (!readLine.isEmpty() && lastTableName != null) {
                List<String> tableLines = result.get(lastTableName);
                Iterator<String> iterator = tableLines.iterator();
                String targetLine = null;
                while (iterator.hasNext()) {
                    String appendLine = iterator.next();
                    if (appendLine.equals(lastLine)) {
                        targetLine = appendLine + readLine;
                        iterator.remove();
                        break;
                    }
                }
                if (targetLine != null) tableLines.add(targetLine);
            }
        }
        return result;
    }

    private synchronized static void appendToFile(String tableName, List<String> lines) {
        String firstLine = lines.get(0);
        String[] sqlArr = firstLine.split("VALUES");
        String insertSql = sqlArr[0] + "VALUES";
        int batchSize = covertConfig.getBatchSize();
        Iterator<String> iterator = lines.iterator();
        File outFile = new File(covertConfig.getOutPath() + File.separator + tableName + ".sql");
        FileWriter fileWriter = new FileWriter(outFile);
        if (!cn.hutool.core.io.FileUtil.exist(outFile)) {
            cn.hutool.core.io.FileUtil.touch(outFile);
            if (covertConfig.isPrintRunSql()) {
                System.out.println("`" + covertConfig.getOutPath() + File.separator + outFile.getName() + ";");
            }
            if (covertConfig.isTruncate()) {
                fileWriter.append("truncate table \"" + covertConfig.getTargetDatabaseName() + "\".\"" + tableName + "\";\n");
            }
        }
        while (iterator.hasNext()) {
            StringBuilder batchContent  = new StringBuilder("\n"+insertSql);
            for (int i = 0; i < batchSize; i++) {
                String line = iterator.next();
                String replace = line.replace(insertSql, "\n").replace(";", ",");
                batchContent.append(replace);
                iterator.remove();
                if (!iterator.hasNext()) break;
            }
            char c = batchContent.charAt(batchContent.length() - 1);
            if (c==','){
                batchContent.deleteCharAt(batchContent.length() - 1);
            }
            fileWriter.append(batchContent.toString().replace(covertConfig.getSourceDatabaseName(), covertConfig.getTargetDatabaseName())+";\n");
        }
    }


}
